package com.kestrel.space.core.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import com.kestrel.space.common.constants.COSConstants;
import com.kestrel.space.common.model.User;
import com.kestrel.space.common.utils.DOCXConvertToPDF;
import com.kestrel.space.common.utils.Generator;
import com.kestrel.space.common.utils.IOConvert;
import com.kestrel.space.common.utils.MyStringUtils;
import com.kestrel.space.core.model.MyFile;
import com.kestrel.space.core.service.CosService;
import com.kestrel.space.core.service.FileService;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author yebolin
 * @date 2024/8/26
 */
@Service
public class CosServiceImpl implements CosService {

    @Autowired
    private COSClient cosClient;

    @Override
    public String upload(MultipartFile file, String type, User user) {
        type = chooseType(type);
        try {
            String originalFileName = file.getOriginalFilename();
            InputStream inputStream = file.getInputStream();
            assert originalFileName != null;
            String filePath = getFileKey(originalFileName, type, user.getId());
            cosClient.putObject(new PutObjectRequest(COSConstants.BUCKET_NAME, filePath, inputStream, null));
            cosClient.setBucketAcl(COSConstants.BUCKET_NAME, CannedAccessControlList.PublicRead);
            return filePath;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            cosClient.shutdown();
        }
        return "文件上传失败";
    }

    @Override
    public String uploadByInputStream(InputStream in, String fileName, String type, User user){
        type = chooseType(type);
        try {
            String filePath = getFileKey(fileName, type, user.getId());
            cosClient.putObject(new PutObjectRequest(COSConstants.BUCKET_NAME, filePath, in, null));
            cosClient.setBucketAcl(COSConstants.BUCKET_NAME, CannedAccessControlList.PublicRead);
            return filePath;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            cosClient.shutdown();
        }
        return "文件上传失败";
    }

    @Override
    public void download(String fileUrl, HttpServletRequest request, HttpServletResponse response) {
        if (!StrUtil.isBlank(fileUrl)) {
            try {
                GetObjectRequest getObjectRequest = new GetObjectRequest(COSConstants.BUCKET_NAME, fileUrl);
                // 直接获取对象，从结果中获取元数据
                COSObject cosObject = cosClient.getObject(getObjectRequest);
                ObjectMetadata objectMetadata = cosObject.getObjectMetadata();

                // 设置响应头，让浏览器识别为下载文件
                response.setContentType(objectMetadata.getContentType());
                // 假设文件 URL 的最后一部分是文件名，可以从 fileUrl 中提取
                String  fileName= new String((fileUrl.substring(fileUrl.lastIndexOf('/') + 1)).getBytes("UTF-8"), "ISO-8859-1");
                response.setContentType("application/force-download");// 设置强制下载不打开
                response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);

                InputStream inputStream = cosObject.getObjectContent();
                OutputStream os = response.getOutputStream();

                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }

                inputStream.close();
                os.flush();
                os.close();

            } catch (IOException e) {
                e.printStackTrace();
                try {
                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "下载失败");
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    @Override
    public String delete(String fileUrl) {
        cosClient.deleteObject(COSConstants.BUCKET_NAME, fileUrl);
        return "文件删除成功";
    }

    @Override
    public List<MyFile> synchronization(String fileUrl, User user) {
        List<MyFile> listFile = new ArrayList<>();
        if(StrUtil.isNotBlank(fileUrl)){
            try {
                // 生成临时下载链接
                Date expiration = new Date(System.currentTimeMillis() + 60 * 60 * 1000); // 1 小时后过期
                GeneratePresignedUrlRequest generatePresignedUrlRequest =
                        new GeneratePresignedUrlRequest(COSConstants.BUCKET_NAME, fileUrl, HttpMethodName.GET);
                generatePresignedUrlRequest.setExpiration(expiration);
                URL url = cosClient.generatePresignedUrl(generatePresignedUrlRequest);

                try (InputStream in = url.openStream()) {
                    String docxFileName = MyStringUtils.getFileNameByFileUrl(fileUrl);
                    assert docxFileName != null;
                    String pdfFileName = MyStringUtils.convertFileNameType(docxFileName, ".pdf");
                    String pdfCosUrl = "";
                    String docxCosUrl = "";
                    // 复制输入流，以便后续可以多次使用
                    try (ByteArrayOutputStream tempOut = new ByteArrayOutputStream()) {
                        byte[] buffer = new byte[1024];
                        int bytesRead;
                        while ((bytesRead = in.read(buffer))!= -1) {
                            tempOut.write(buffer, 0, bytesRead);
                        }
                        // 使用复制后的流进行上传 docx 文件
                        try (ByteArrayInputStream copyIn = new ByteArrayInputStream(tempOut.toByteArray())) {
                            docxCosUrl = uploadByInputStream(copyIn, docxFileName, "blog/docx", user);
                            listFile.add(new MyFile(docxFileName,"blog/docx",false,0,docxCosUrl,"/root/blog",new Date(),new Date()));
                        }
                        // 使用复制后的流进行转换为 PDF
                        try (ByteArrayInputStream convertIn = new ByteArrayInputStream(tempOut.toByteArray())) {
                            ByteArrayOutputStream out = DOCXConvertToPDF.convert(convertIn);
                            try (ByteArrayInputStream byteIn = new ByteArrayInputStream(out.toByteArray())) {
                                pdfCosUrl = uploadByInputStream(byteIn, pdfFileName, "blog/pdf", user);
                                listFile.add(new MyFile(pdfFileName,"blog/pdf",false,0,pdfCosUrl,"/root/blog",new Date(),new Date()));
                            }
                        }
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            } finally {
                // 关闭 COS 客户端
                cosClient.shutdown();
            }
        }
        return listFile;
    }

    /**
     * 生成文件路径
     */
    private String getFileKey(String originalFileName, String type, Long id) {
        String filePath = type + '/';
        //1.获取后缀名 2.去除文件后缀 替换所有特殊字符
        String fileType = originalFileName.substring(originalFileName.lastIndexOf("."));
        String fileStr = StrUtil.removeSuffix(originalFileName, fileType).replaceAll("[^0-9a-zA-Z\\u4e00-\\u9fa5]", "_");
        filePath += id + "/";
        String dateTime = new DateTime().toString("yyyyMMddHHmmss");
        filePath += dateTime.substring(0, 4) + "/";
        filePath += dateTime.substring(4, 6) + "/";
        filePath += dateTime + "-" + fileStr + fileType;
        return filePath;
    }


    private String chooseType(String type) {
        if (!type.equals(COSConstants.DOCX) && !type.equals(COSConstants.XLXS)
                && !type.equals(COSConstants.PDF) && !type.equals(COSConstants.TXT)
                && !type.equals(COSConstants.MP3) && !type.equals(COSConstants.MP4)
                && !type.equals(COSConstants.JPG) && !type.equals(COSConstants.PNG)
                && !type.equals(COSConstants.RAR) && !type.equals(COSConstants.ZIP)
                && !type.equals(COSConstants.BLOG_DOCX) && !type.equals(COSConstants.BLOG_PDF)
                && !type.equals(COSConstants.DOC) && !type.equals(COSConstants.XLS)
                && !type.equals(COSConstants.PPT) && !type.equals(COSConstants.VSDX)
                && !type.equals(COSConstants.JPEG) && !type.equals(COSConstants.JAR)
        ) {
            type = COSConstants.OTHERS;
        }
        return type;
    }
}
